package com.linkai.JUC._16_cas;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Description: CAS CompareAndSwap：比较并交换
 *                CAS 操作的流程：
 *                      线程在读取数据时不进行加锁，在准备写回数据时，比较原值是否修改，
 *                      若未被其他线程修改则写回，若已被修改，则重新执行读取流程。
 *                如果我的期望值达到了，那么就更新；否则，就不更新；CAS 是CPU的并发原语！
 *                原子类里面的 compareAndSet 方法使用了 Unsafe 类
 *                ABA 问题：对应的实现就是乐观锁
 *                解决ABA问题：
 *                  （1）使用版本号机制
 *                  （2）使用时间戳（适用于sql场景）
 *                原子引用:
 *                  A线程期望是1，然后修改为2；
 *                  B线程先修改为3，然后又修改为1；A线程是完全不知情的，最后的值依旧符合他的期望。
 * @Author: 林凯
 * @Date: 2021/10/26 19:51
 */
public class CASDemo {

    // 1. 测试 CAS
    public static void testCAS() {
        AtomicInteger atomicInteger = new AtomicInteger(2020);

        // 期望，更新
//        public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
        // 如果我的期望值达到了，那么就更新；否则，就不更新；CAS 是CPU的并发原语！
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
        atomicInteger.getAndIncrement();    // 相当于自增1
        System.out.println(atomicInteger.compareAndSet(2022, 2030));
        System.out.println(atomicInteger.get());


    }

    // 2. 测试 ABA 问题
    public static void testABA() {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        // 对于我们平时写的SQL来说：乐观锁！
        // ======================= 捣乱的线程 ================
        atomicInteger.compareAndSet(2020, 2021);
        System.out.println(atomicInteger.get());
        atomicInteger.compareAndSet(2021, 2020);
        System.out.println(atomicInteger.get());

        // ===================== 期望的线程 ===============
        // 该线程不知道另一个线程已经修改过值了
        atomicInteger.compareAndSet(2020, 6666);
        System.out.println(atomicInteger.get());

    }

    // 3. 测试原子引用（版本号）
    public static void test() {
        // AtomicStampedReference 注意：如果泛型是要给包装类，注意对象应用问题（这里可以把值设置在127以内）
        // 正常业务中，这里比较的是一个对象，对象的引用一定是唯一的
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);

        Lock lock = new ReentrantLock();

        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();      // 获得版本号
            System.out.println("a1:" + stamp );
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 保证把版本号更新了
            System.out.println(atomicStampedReference.compareAndSet(1, 2,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("a2:" + atomicStampedReference.getStamp());
            //  又把这个值给改回去，但是要保证版本号在更新
            System.out.println(atomicStampedReference.compareAndSet(2, 1,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("a3:" + atomicStampedReference.getStamp());


        }, "A").start();

        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();      // 获得版本号
            System.out.println("b1:" + stamp);
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 这里会设置失败，因为期望的值相同，但是期望的 stamp 不同；线程A修改是，stamp 会自增，导致这里 stamp 不一致
            System.out.println(atomicStampedReference.compareAndSet(1, 8, stamp, stamp + 1));
            System.out.println("b2:" + atomicStampedReference.getStamp());

        }, "B").start();


    }

    // CAS  CompareAndSet：比较并交换
    public static void main(String[] args) {
        testCAS();
        System.out.println("*********************");
        testABA();
        System.out.println("*********************");
        test();
    }
}
